#include <iostream>
using namespace std;
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
const int argvnum = 64;
const int envnum = 64;

//全局的命令行参数
char* gargv[argvnum];
int gargc = 0;
//我的系统的环境变量
char* genv[envnum];
//全局变量
int lastcode = 0;

const int basesize = 1024;
//全局的当前shell的工作路径
char pwd[basesize];
char pwdenv[basesize+5];
// 全局变量与重定向有关
#define NoneRedir   0
#define InputRedir  1
#define OutputRedir 2
#define AppRedir    3

int redir = NoneRedir;
char *filename = nullptr;

// "    "file.txt
#define TrimSpace(pos) do{\
    while(isspace(*pos)){\
        pos++;\
    }\
}while(0)

string GetUserName()
{
    // string name = getenv("USER");
    // return name.empty()?"None":name;
    const char* name = getenv("USER");
    return (name==nullptr)?"None":string(name);
}

string GetHostName()
{
    // string hostname = getenv("HOSTNAME");
    // return hostname.empty()?"None":hostname;
    const char* hostname = getenv("USER");
    return (hostname==nullptr)?"None":string(hostname);
}

string GetPwd()
{
    if(nullptr==getcwd(pwd,sizeof(pwd)))
        return "None";
    snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
    putenv(pwdenv);
    return pwd;
}

string LastDir()
{
    string cur = GetPwd();
    if(cur=="/"||cur=="None")
        return cur;
    size_t pos = cur.rfind("/");
    if(pos==std::string::npos)
        return cur;
    return cur.substr(pos+1);
}
string MakeCommandLine()
{
    char command_line[basesize];
    snprintf(command_line,sizeof(command_line),"[%s@%s %s]# ",\
            GetUserName().c_str(),GetHostName().c_str(),LastDir().c_str());
    return command_line;
}

void PrintCommandLine()
{
    printf("%s",MakeCommandLine().c_str());
    fflush(stdout);
}

bool GetCommandLine(char command_buffer[],int size)//2.获取用户指令
{
    // ls -l
    char* result = fgets(command_buffer,size,stdin);
    if(!result)
    {
        return false;
    }
    command_buffer[strlen(command_buffer)-1] = 0;
    if(strlen(command_buffer)==0)
        return false;
    return true;
}

void ResetCommandLine()
{
    memset(gargv,0,sizeof(gargv));
    gargc = 0;
    redir = NoneRedir;
    filename = nullptr;
}
void PraseRedir(char command_buffer[],int len)
{
    int end = len-1;
    while(end>=0)
    {
        if(command_buffer[end]=='<')
        {
            redir = InputRedir;
            command_buffer[end] = 0;
            filename = &command_buffer[end]+1;
            TrimSpace(filename);
            break;
        }
        else if(command_buffer[end]=='>')
        {
            if(command_buffer[end-1]=='>')
            {
                redir = AppRedir;
                command_buffer[end] = 0;
                command_buffer[end-1] = 0;
                filename = &command_buffer[end]+1;
                TrimSpace(filename);
                break;
            }
            else
            {
                redir= OutputRedir;
                command_buffer[end] = 0;
                filename = &command_buffer[end]+1;
                TrimSpace(filename);
                break;
            }
        }
        else
        {
            end--;
        }
    }
}
void PraseCommandLine(char command_buffer[],int len)//3.分析命令
{
    (void)len;
    memset(gargv,0,sizeof(gargv));
    gargc = 0;
    
    redir = NoneRedir;
    filename = nullptr;

    PraseRedir(command_buffer,len);

    const char* sep = " ";
    gargv[gargc++] = strtok(command_buffer,sep);

    while((bool)(gargv[gargc++]=strtok(nullptr,sep)))
    {

    }
    //-------
    gargc--;
}

void debug()
{
    printf("argc: %d\n",gargc);
    for(int i = 0;gargv[i];i++)
    {
        printf("argv[%d]: %s\n",i,gargv[i]);
    }
}

void DoRedir()
{
    if(redir==InputRedir)
    {
        if(filename)
        {
            int fd = open(filename,O_RDONLY);
            if(fd<0)
            {
                exit(2);
            }
            dup2(fd,0);
        }
        else
        {
            exit(1);
        }

    }
    else if(redir==OutputRedir)
    {
        if(filename)
        {
            int fd = open(filename,O_CREAT|O_WRONLY|O_TRUNC,0666);
            if(fd<0)
            {
                exit(4);
            }
            dup2(fd,1);
        }
        else
        {
            exit(3);
        }

    }
    else if(redir==AppRedir)
    {
        if(filename)
        {
            int fd = open(filename,O_CREAT|O_WRONLY|O_APPEND,0666);
            if(fd<0)
            {
                exit(6);
            }
            dup2(fd,1);
        }
        else
        {
            exit(5);
        }
    }
    else
    {
        //没有重定向
    }
}

bool ExecuteCommand()//4.执行命令
{
    pid_t id = fork();
    if(id<0)
        return false;

    if(id==0)
    {
        //子进程
        DoRedir();
        //1.执行命令
        execvpe(gargv[0],gargv,genv);
        exit(1);
    }
    int status = 0;
    pid_t rid = waitpid(id,&status,0);
    if(rid>0)
    {
        if(WIFEXITED(status))
        {
            lastcode = WEXITSTATUS(status);
        }
        else
        {
            lastcode = 100;
        }
        return true;
    }
    return false;        
}

void AddEnv(const char* item)
{
    int index = 0;
    while(genv[index])
    {
        index++;
    }
    genv[index] = (char*)malloc(strlen(item)+1);
    strncpy(genv[index],item,strlen(item)+1);
    genv[++index] = nullptr;
}
//shell自己执行命令
bool CheckAndExecBuiltCommand()
{
    if(strcmp(gargv[0],"cd")==0)
    {
        if(gargc==2)
        {
            chdir(gargv[1]);
            lastcode = 0;
        }
        else
        {
            lastcode = 1;
        }
        return true;
    }
    else if(strcmp(gargv[0],"export")==0)
    {
        if(gargc==2)
        {
            AddEnv(gargv[1]);
            lastcode = 0;
        }
        else
        {
            lastcode = 2;
        }
        return true;
    }
    else if(strcmp(gargv[0],"echo")==0)
    {
        if(gargc==2)
        {
            //echo $?
            //echo $PATH
            if(gargv[1][0]=='$')
            {
                if(gargv[1][1]=='?')
                {
                    printf("%d\n",lastcode);
                    lastcode = 0;
                }
                
            }
            else
            {
                printf("%s\n",gargv[1]);
                lastcode = 0;
            }
        }
        else
        {
            lastcode = 3;
        }
        return true;
    }
    return false;
}

void InitEnv()
{
    extern char **environ;
    int index = 0;
    while(environ[index])
    {
        genv[index] = (char*)malloc(strlen(environ[index]+1));
        strncpy(genv[index],environ[index],strlen(environ[index]+1));
        index++;
    }
    genv[index] = nullptr;
}

int main()
{
    InitEnv();
    char command_buffer[basesize];
    while(true)
    {
        PrintCommandLine();
        if(!GetCommandLine(command_buffer,basesize))
        {
            continue;
        }
        PraseCommandLine(command_buffer,strlen(command_buffer));
        if(CheckAndExecBuiltCommand())
        {
            continue;
        }
        ExecuteCommand();

    }
    return 0;
}